package com.xmlit.project.engine.kxsd;

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java1.util.regex.Pattern;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.ws.jaxme.xs.XSAnnotation;
import org.apache.ws.jaxme.xs.XSAppinfo;
import org.apache.ws.jaxme.xs.XSElement;
import org.apache.ws.jaxme.xs.XSGroup;
import org.apache.ws.jaxme.xs.XSParser;
import org.apache.ws.jaxme.xs.XSParticle;
import org.apache.ws.jaxme.xs.XSSchema;
import org.apache.ws.jaxme.xs.impl.XSObjectImpl;
import org.apache.ws.jaxme.xs.xml.XsTElement;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.xmlit.project.engine.struct.Struct;
import com.xmlit.project.engine.struct.StructSequence;
import com.xmlit.project.engine.struct.StructSimple;
import com.xmlit.project.engine.struct.impl.StructChoiceImpl;
import com.xmlit.project.engine.struct.impl.StructImpl;
import com.xmlit.project.engine.struct.impl.StructSequenceImpl;
import com.xmlit.project.engine.struct.impl.StructSimpleImpl;

public class KXSDReader {

	public static Struct readSchema(String schemaString) throws Exception,
			SAXException, ParserConfigurationException {
		StringReader schema = new StringReader(schemaString);
		InputSource source = new InputSource(schema);
		XSParser xsParser = new XSParser();
		xsParser.setValidating(false);
		XSSchema xsSchema = xsParser.parse(source);

		XSElement root = xsSchema.getElements()[0];
		return readElement(root);
	}

	public static Struct readElement(XSElement element) throws Exception {
		Struct current = null;
		boolean mandatory = true;
		if (element.getType().isSimple()) {
			current = new StructSimpleImpl(element.getName().getLocalName());
			mandatory = handleSimple((StructSimple) current, element);
			// System.out.println(element.getName() + "  "
			// +element.getType().getSimpleType().getPattern());
		} else {
			XSGroup group = element.getType().getComplexType().getParticle()
					.getGroup();

			if ("sequence".equals("" + group.getCompositor())) {
				current = new StructSequenceImpl(element.getName()
						.getLocalName());
				mandatory = handleSequence((StructSequence) current, element);
			} else if ("choice".equals("" + group.getCompositor())) {
				current = new StructChoiceImpl(element.getName().getLocalName());
			} else if ("all".equals("" + group.getCompositor())) {
				throw new RuntimeException("Choice is not supported yet");
			}

			// System.out.println(element.getName());
			for (XSParticle child : group.getParticles()) {
				current.addChild(readElement(child.getElement()));
			}
		}
		XsTElement xele = getXsTElement(element);
		if (mandatory) {
			current.setMinOccurrences(xele.getMinOccurs());
		} else {
			current.setMinOccurrences(0);
		}
		if (xele.getMaxOccurs() == -1) {
			current.setMaxOccurrences(100);
		} else {
			current.setMaxOccurrences(xele.getMaxOccurs());
		}

		return current;
	}

	private static boolean handleSequence(StructSequence current,
			XSElement element) {
		Document annotation = getAnnotationDocuments(element);
		if (annotation != null) {
			String result = annotation.getDocumentElement().getAttribute(
					"mandatory");
			Element separator = null;
			for (int i = 0; i < annotation.getDocumentElement().getChildNodes()
					.getLength(); i++) {
				if (annotation.getDocumentElement().getChildNodes().item(i) instanceof Element) {
					separator = (Element) annotation.getDocumentElement()
							.getChildNodes().item(i);
				}
			}
			if (separator != null) {
				String sep = separator.getAttribute("Separator");
				sep = handleEscape(sep);
				//sep = StructImpl.quote(sep);
				//sep = sep + getRightContext(separator);
				String sepReg = sep + getRightContext(separator);
				if (sep != null && !"".equals(sep)) {
					current.setDelimiter(sep);
					if (!sepReg.equals(sep)) {
						current.setDelimiterRegex(sepReg);
					}
					
				}

				String after = separator.getAttribute("Also_After_Last_Child");
				if (after != null && "true".equals(after)) {
					current.setSuffix(sep);
				}

				String before = separator
						.getAttribute("Also_Before_First_Child");
				if (before != null && "true".equals(before)) {
					current.setSuffix(sep);
				}
			}
			return "true".equals(result);
		}

		return true;

	}

	private static String getRightContext(Element sep) {
		Element rContext = (Element) sep.getElementsByTagName("right_context").item(0);
		if (rContext != null) {
			String constraint = rContext.getAttribute("String_Constraint");
			boolean isNegative = "true".equals(rContext.getAttribute("Is_Negative_Constraint"));
			if (isNegative) {
				return "(?!"+constraint+")";
			}
		}
		
		return "";
	}

	private static boolean handleSimple(StructSimple current, XSElement element)
			throws Exception {
		Document annotation = getAnnotationDocuments(element);
		if (annotation != null) {
			String result = annotation.getDocumentElement().getAttribute(
					"mandatory");

			String constVal = annotation.getDocumentElement().getAttribute(
					"Constant_Value");
			constVal = handleEscape(constVal);
			if (constVal != null && !"".equals(constVal)) {
				current.setSimplePattern(Pattern.quote(constVal));
				return "true".equals(result);
			}
			String validVals = annotation.getDocumentElement().getAttribute(
					"Valid_Values");
			if (validVals != null && !"".equals(validVals)) {
				String[] vals = validVals.split("=,");
				StringBuffer res = new StringBuffer();
				for (int i = 0; i < vals.length; i++) {
					res.append(Pattern.quote(handleEscape(vals[i])));
					if (i + 1 < vals.length)
						res.append("|");
				}
				current.setSimplePattern(res.toString());
				return "true".equals(result);
			}
		}

		String[] pattern = element.getType().getSimpleType().getPattern()[0];
		if (pattern != null) {
			current.setSimplePattern(pattern[0]);
		}

		return true;

	}

	private static String handleEscape(String string) {
		return string.replace("\\r\\n", "\r\n");
	}

	private static Document getAnnotationDocuments(XSElement element) {// to
		// change
		if (element.getAnnotations().length > 0) {
			XSAnnotation xsAnnotation = element.getAnnotations()[0];
			XSAppinfo[] xsAppinfos = xsAnnotation.getAppinfos();
			XSAppinfo xsAppinfo = xsAppinfos[0];
			// System.out.println(getAnnotationDocuments(xsAppinfo));

			for (int i = 0; i < xsAppinfo.getChilds().length; i++) {
				if (xsAppinfo.getChilds()[i] instanceof Document)
					return (Document) xsAppinfo.getChilds()[i];
			}
		}
		return null;

	}

	private static XsTElement getXsTElement(XSElement element) {// to change
		XsTElement base = null;
		try {
			Field field = XSObjectImpl.class.getDeclaredField("baseObject");
			field.setAccessible(true);
			base = (XsTElement) field.get(element);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return base;
	}
}
